//
// WinPcap.net
//
// Copyright (C) 2005-2007 Matthew Ward
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// Matthew Ward (mrward@users.sourceforge.net)

using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;

namespace WinPcap
{
	/// <summary>
	/// A network data packet.
	/// </summary>
	public class Packet
	{
		/// <summary>
		/// The number of bytes in 32 bits.
		/// </summary>
		public const int BytesIn32Bits = 4;

		/// <summary>
		/// The packet's bytes.
		/// </summary>
		byte[ ] m_Bytes;
			
		/// <summary>
		/// Creates a new instance of the <see cref="Packet"/> class.
		/// </summary>
		/// <param name="bytes">The bytes of the packet.</param>
		public Packet( byte[ ] bytes )
			: this( bytes, 0, bytes.Length )
		{
		}

		/// <summary>
		/// Creates a new instance of the <see cref="Packet"/> class.
		/// </summary>
		/// <param name="bytes">The bytes of the packet.</param>
		/// <param name="startIndex">The index of the byte array
		/// to copy the packet's bytes from.</param>
		public Packet( byte[ ] bytes, int startIndex )
			: this( bytes, startIndex, bytes.Length - startIndex )
		{
		}
		
		/// <summary>
		/// Creates a new instance of the <see cref="Packet"/> class.
		/// </summary>
		/// <param name="bytes">The bytes of the packet.</param>
		/// <param name="startIndex">The index of the byte array
		/// to copy the packet's bytes from.</param>
		/// <param name="length">The amount of bytes in the byte array
		/// that make up the packet.</param>
		public Packet( byte[ ] bytes, int startIndex, int length )
		{
			Debug.Assert( bytes.Length >= ( startIndex + length ),
				"Index and length are larger than the number of bytes in the buffer." );

			m_Bytes = new Byte[ length ];
	
			for( int i = 0; i < length; ++i )
			{
				m_Bytes[ i ] = bytes[ i + startIndex ];
			}
		}
		
		/// <summary>
		/// Gets the bytes as a formatted string.
		/// </summary>
		/// <remarks><para>The returned string is of the form:</para>
		/// <code>
		/// 0000  01 02 03 04 05 06 07 08  AA BB CC DD FF FF FF FF
		/// 0010  AA BB BB AA CC DD DD CC
		/// </code>
		/// </remarks>
		/// <returns>A hex string.
		/// </returns>
		public string RawHex
		{
			get
			{
				return GetRawHex( m_Bytes, m_Bytes.Length );
			}
		}

		/// <summary>
		/// Gets the bytes for this packet.
		/// </summary>
		public byte[ ] GetBytes( )
		{
			return m_Bytes;
		}

		/// <summary>
		/// Gets the packet contained inside this packet.
		/// </summary>
		/// <remarks>
		/// If this packet is an Ethernet Packet then the inner packet
		/// might be an <see cref="IPPacket"/>.
		/// </remarks>
		public virtual Packet InnerPacket
		{
			get
			{
				return null;	
			}
		}
		
		/// <summary>
		/// Gets the bytes as a formatted string.
		/// </summary>
		/// <remarks><para>The returned string is of the form:</para>
		/// <code>
		/// 0000  01 02 03 04 05 06 07 08  AA BB CC DD FF FF FF FF
		/// 0010  AA BB BB AA CC DD DD CC
		/// </code>
		/// </remarks>
		/// <param name="bytes">The bytes to format as a string.</param>
		/// <param name="length">The number of bytes to format as a string.</param>
		/// <returns>A hex string.</returns>
		public static string GetRawHex( byte[ ] bytes, int length )
		{
			StringBuilder Hex = new StringBuilder( );
	
			Hex.Append( "0000  " );
	
			int BytesPerLine = 16;
			int NewLineByteCount = BytesPerLine;
			int HalfLineByteCount = BytesPerLine / 2;
		
			for( int i = 0; i < length; ++i )
			{
				if( NewLineByteCount == i )
				{
					Hex.Append( "\r\n" );
					Hex.Append( i.ToString( "X4", CultureInfo.InvariantCulture ) );
					Hex.Append( "  " );
	
					NewLineByteCount += BytesPerLine;
				}
				else if( HalfLineByteCount == i )
				{
					Hex.Append( " " );
	
					HalfLineByteCount += BytesPerLine;
				}
	
				Byte CurrentByte = bytes[ i ];
				Hex.Append( CurrentByte.ToString( "X2", CultureInfo.InvariantCulture ) );
				Hex.Append( " " );
			}
	
			return Hex.ToString( );
		}		

		/// <summary>
		/// Gets an unsigned short from a byte array.
		/// </summary>
		/// <param name="bytes">The byte array.</param>
		/// <param name="index">The index into the array.</param>
		/// <returns>The unsigned short at the specified index.</returns>
		[ CLSCompliant( false ) ] 
		protected static ushort GetUInt16( byte[ ] bytes,
			int index )
		{
			// Validate input parameters.
			Debug.Assert( ( index + 1 ) < bytes.Length,
				"Byte array not long enough to support specified index." );

			// Convert the hex byte array into a decimal value.

			int Value = SumBytesAsDecimal( bytes, index, Marshal.SizeOf( typeof( ushort ) ) );

			ushort UnsignedShortValue = ( ushort )Value;

			return UnsignedShortValue;
		}

		/// <summary>
		/// Gets an int from a byte array.
		/// </summary>
		/// <param name="bytes">The byte array.</param>
		/// <param name="index">The index into the array.</param>
		/// <returns>The int at the specified index.</returns>
		[ CLSCompliant( false ) ] 
		protected static uint GetUInt32( byte[ ] bytes,
			int index )
		{
			// Validate input parameters.
			Debug.Assert( ( index + 1 ) < bytes.Length,
				"Byte array not long enough to support specified index." );

			// Convert the hex byte array into a decimal value.
	
			uint Value = ( uint )SumBytesAsDecimal( bytes, 
				index, 
				Marshal.SizeOf( typeof( uint ) ) );
	
			return Value;
		}

		/// <summary>
		/// Gets the high order part of the byte.
		/// </summary>
		/// <param name="value"></param>
		/// <returns>The high order part of the byte.</returns>
		protected static int GetHighOrderNibble( byte value )
		{
			return ( ( value & 0xF0 )  >> 4 );
		}
		
		/// <summary>
		/// Gets the low order part of the byte.
		/// </summary>
		/// <param name="value"></param>
		/// <returns>The low order part of the byte.</returns>
		protected static int GetLowOrderNibble( byte value )
		{
			return value & 0x0F;
		}
			
		/// <summary>
		/// Gets a new byte array from an existing one.
		/// </summary>
		/// <param name="bytes">The byte array to get the data from.</param>
		/// <param name="startIndex">The index where to start extracting
		/// the bytes from.</param>
		/// <param name="amount">The number of bytes to copy.</param>
		/// <returns>A byte array.</returns>
		protected static byte[ ] GetBytesFromByteArray( byte[ ] bytes,
			int startIndex, 
			int amount )
		{
			Debug.Assert( ( startIndex + amount ) <= bytes.Length );
		
			byte[ ] CreatedBytes = new byte[ amount ];
	
			int BytesAdded = 0;
	
			for( int i = startIndex; i < ( startIndex + amount ); ++i )
			{
				CreatedBytes[ BytesAdded ] = bytes[ i ];
				++BytesAdded;
			}
	
			return CreatedBytes;
		}

		/// <summary>
		/// Sums bytes as a decimal number.  If we have two bytes FF FF then 
		/// this will sum to 65535.
		/// </summary>
		/// <param name="bytes">A byte array.</param>
		/// <param name="index">The index of the byte array
		/// to start the summing from.</param>
		/// <param name="byteCount">How many bytes to sum.</param>
		/// <returns>The bytes as a decimal.</returns>
		protected static int SumBytesAsDecimal( byte[ ] bytes, int index, int byteCount )
		{
			int Value = 0;

			int CurrentByteIndex = index + ( byteCount - 1 );
	
			for( int i = 0; i < byteCount; ++i )
			{
				int MultiplicationFactor = ( int )Math.Pow( Byte.MaxValue + 1, i );
	
				byte CurrentByte = bytes[ CurrentByteIndex ];
	
				if( i == 0 )
				{
					Value += CurrentByte;
				}
				else
				{
					Value += CurrentByte * MultiplicationFactor;
				}
	
				--CurrentByteIndex;
			}
	
			return Value;

		}		
	}
}
